home *** CD-ROM | disk | FTP | other *** search
/ 3D Images / 3D Images.iso / programs / amiga / rayshade / libshade / shade.c < prev    next >
C/C++ Source or Header  |  1995-01-12  |  13KB  |  425 lines

  1. /*
  2.  * shade.c
  3.  *
  4.  * Copyright (C) 1989, 1991, Craig E. Kolb
  5.  * All rights reserved.
  6.  *
  7.  * This software may be freely copied, modified, and redistributed
  8.  * provided that this copyright notice is preserved on all copies.
  9.  *
  10.  * You may not distribute this software, in whole or in part, as part of
  11.  * any commercial product without the express consent of the authors.
  12.  *
  13.  * There is no warranty or other guarantee of fitness of this software
  14.  * for any purpose.  It is provided solely "as is".
  15.  *
  16.  * shade.c,v 4.1 1994/08/09 08:04:57 explorer Exp
  17.  *
  18.  * shade.c,v
  19.  * Revision 4.1  1994/08/09  08:04:57  explorer
  20.  * Bump version to 4.1
  21.  *
  22.  * Revision 1.1.1.1  1994/08/08  04:52:17  explorer
  23.  * Initial import.  This is a prerelease of 4.0.6enh3, or 4.1 possibly.
  24.  *
  25.  * Revision 4.0  91/07/17  14:47:36  kolb
  26.  * Initial version.
  27.  * 
  28.  */
  29. #include "rayshade.h"
  30. #include "libtext/texture.h"
  31. #include "libsurf/surface.h"
  32. #include "liblight/light.h"
  33. #include "libsurf/atmosphere.h"
  34. #include "options.h"
  35. #include "stats.h"
  36. #include "viewing.h"
  37.  
  38. Medium    TopMedium;
  39. Atmosphere *AtmosEffects;
  40.  
  41. static void shade(), LightRay(), Lighting(), ReflectRay();
  42. static int TransmitRay();
  43.  
  44. /*
  45.  * Calculate color of ray.
  46.  */
  47. void
  48. ShadeRay(hitlist, ray, dist, back, color, contrib)
  49. HitList *hitlist;        /* Information about point of intersection. */
  50. Ray *ray;            /* Direction and origin of ray. */
  51. Float dist;            /* Distance from origin of intersection. */
  52. Color    *back,            /* "Background" color */
  53.     *color,            /* Color to assign current ray. */
  54.     *contrib;        /* Contribution of this ray to final color */
  55. {
  56.     Vector norm, gnorm, pos; /* surface normal, point of intersection */
  57.     Surface surf, *stmp;    /* surface properties */
  58.     int enter, smooth;    /* entering ?, gnorm != snorm ?*/
  59.  
  60.     if (hitlist->nodes == 0) {
  61.         /*
  62.          * No valid intersection.  Set distance for atmospheric
  63.          * effects and set color of ray to background.
  64.          */
  65.         *color = *back;
  66.         VecAddScaled(ray->pos, FAR_AWAY, ray->dir, &pos);
  67.         if (!ray->media && AtmosEffects)
  68.             Atmospherics(AtmosEffects, ray, FAR_AWAY, &pos, color);
  69.         return;
  70.     }
  71.  
  72.     /*
  73.      * Compute normal, surface properties, etc.
  74.      */
  75.     stmp = GetShadingSurf(hitlist);
  76.     surf = *stmp;
  77.     enter = ComputeSurfProps(hitlist, ray, &pos, &norm, &gnorm, &surf,
  78.             &smooth);
  79.     Stats.HitRays++;
  80.  
  81.     /*
  82.      * Calculate ray color.
  83.      */
  84.     shade(&pos, ray, &norm, &gnorm, smooth, enter, &surf, back, color,
  85.             contrib);
  86.     if (!ray->media && AtmosEffects)
  87.         Atmospherics(AtmosEffects, ray, dist, &pos, color);
  88. }
  89.  
  90. /*
  91.  * Perform lighting calculations based on surface normal & other properties,
  92.  * incident ray direction and position, and light source properties.
  93.  * Spawn any necessary reflected and transmitted rays.
  94.  */
  95. static void
  96. shade(pos, ray, nrm, gnrm, smooth, enter, surf, back, color, contrib)
  97. Vector *pos, *nrm, *gnrm;    /* hit pos, shade normal, geo normal */
  98. int smooth;            /* true if shading norm and geo norm differ */
  99. int enter;            /* TRUE if entering surface */
  100. Ray *ray;            /* indicent ray */
  101. Surface *surf;            /* properties of hit surface */
  102. Color *back, *color;        /* background color, computed color */
  103. Color *contrib;            /* contribution to final pixel value */
  104. {
  105.     Float    k;        /* -ray . normal */
  106.     Color    newcontrib;
  107.     Vector    refl;        /* reflected direction */
  108.     Color    reflectivity,    /* effective surface reflectivity */
  109.         intens;        /* reflected/transmitted intensity */
  110.     Light *lp;        /* current light source */
  111.     extern Light *Lights;    /* list of defined sources */
  112.  
  113.     /*
  114.      * Ambient color is always included.
  115.      */
  116.     ColorMultiply(surf->amb, Options.ambient, color);
  117.  
  118.     /*
  119.      * Calculate direction of reflected ray.
  120.      */
  121.     k = -dotp(&ray->dir, nrm);
  122.     VecAddScaled(ray->dir, 2.*k, *nrm, &refl);
  123.  
  124.     /*
  125.      * Calculate intensity contributed by each light source.
  126.      */
  127.     for (lp = Lights; lp; lp = lp->next)
  128.         LightRay(lp, pos, nrm, gnrm, smooth, &refl, surf, ray, color);
  129.  
  130.     if (ray->depth >= Options.maxdepth)
  131.         /*
  132.          * Don't spawn any transmitted/reflected rays.
  133.          */
  134.         return;
  135.     /*
  136.      * Specular transmission (refraction).
  137.      */
  138.     ColorScale(surf->reflect, surf->spec, &reflectivity);
  139.  
  140.     if (surf->transp > EPSILON) {
  141.         ColorScale(surf->transp, surf->body, &intens);
  142.         ColorMultiply(intens, *contrib, &newcontrib);
  143.         if (newcontrib.r > Options.cutoff.r ||
  144.             newcontrib.g > Options.cutoff.g ||
  145.             newcontrib.b > Options.cutoff.b)
  146.             /*
  147.              * Transmit ray.  If TIR occurs, add transmitted
  148.              * component to reflected component.  Kinda strange, but...
  149.              */
  150.             if (TransmitRay(ray, pos, nrm, k, surf->index,
  151.                 surf->statten, enter, back, &newcontrib, &intens, color))
  152.                 ColorAdd(reflectivity, intens, &reflectivity);
  153.     }
  154.  
  155.     if (reflectivity.r > EPSILON ||
  156.         reflectivity.g > EPSILON ||
  157.         reflectivity.b > EPSILON) {
  158.         ColorMultiply(reflectivity, *contrib, &newcontrib);
  159.         if (newcontrib.r > Options.cutoff.r ||
  160.             newcontrib.g > Options.cutoff.g ||
  161.             newcontrib.b > Options.cutoff.b)
  162.             ReflectRay(ray, pos, &refl, back, &reflectivity,
  163.                 &newcontrib, color);
  164.     }
  165. }
  166.  
  167. /*
  168.  * Lighting calculations
  169.  */
  170. static void
  171. LightRay(lp, pos, norm, gnorm, smooth, reflect, surf, oldray, color)
  172. Light *lp;            /* Light source */
  173. Vector *pos, *norm, *gnorm;    /* hit pos, shade norm, geo norm */
  174. int smooth;            /* true if shade and geo norm differ */
  175. Vector *reflect;        /* reflection direction */
  176. Surface *surf;            /* surface characteristics */
  177. Ray *oldray;            /* indicent ray */
  178. Color *color;            /* resulting color */
  179. {
  180.     Color lcolor;
  181.     Ray newray;
  182.     Float costheta, cosalpha, cosphi, dist;
  183.  
  184.     newray.type = SHADOW_RAY;
  185.     newray.pos = *pos;
  186.     newray.depth = oldray->depth;
  187.     newray.sample = oldray->sample;
  188.     newray.time = oldray->time; 
  189.     newray.media = (Medium *)NULL;    
  190.  
  191.     LightDirection(lp, pos, &newray.dir, &dist);
  192.  
  193.     costheta = dotp(&newray.dir, norm);
  194.  
  195. /* ray viewpoint is the position at the light source */
  196.         VecAddScaled(newray.pos, dist, newray.dir, &newray.viewpoint);
  197. /* shadow ray "sees" same area at intersection position as parent ray */
  198.         cosphi = -dotp(&oldray->dir, norm);
  199.         if (cosphi < EPSILON) cosphi = EPSILON;
  200.         newray.width = PixelSize(oldray, VecDist(&newray.pos, &oldray->pos)) / cosphi * costheta;
  201. /* shadow rays become smaller as they approach the light source */
  202.         newray.viewdist = -dist;   
  203.         newray.stretch = 1.;
  204.  
  205.     if (smooth) {
  206.         cosalpha = dotp(&newray.dir, gnorm); 
  207.         /*
  208.          * If shading normal indicates self-shadowing
  209.          * and geom normal indicates no self-shadowing,
  210.          * trust the geom normal.
  211.          */
  212.         if (costheta <= 0. && cosalpha > 0.)
  213.             costheta = cosalpha;
  214.         /*
  215.          * If geom normal indicates self-shadowing and
  216.          * geom normal doesn't, then have to do something
  217.          * clever ala Snyder & Barr.
  218.          */
  219.     }
  220.  
  221.     if (costheta <= 0.) {
  222.         /*
  223.          * Light source is on opposite side of surface,
  224.          * hence light must be transmitted through...
  225.          */
  226.         if (surf->translucency < EPSILON)
  227.             return;
  228.         if (!LightIntens(lp, &newray, dist,
  229.             (int)surf->noshadow, &lcolor))
  230.             return;
  231.         cosalpha = dotp(&oldray->dir, &newray.dir);
  232.         ColorScale(surf->translucency, lcolor, &lcolor);
  233.         Lighting(-costheta, cosalpha, &lcolor, &surf->translu,
  234.                 &surf->body, surf->stexp, color);
  235.     } else {
  236.         if (!LightIntens(lp, &newray, dist,
  237.             (int)surf->noshadow, &lcolor))
  238.             return;  /* prim is in shadow w.r.t light source */
  239.  
  240.         cosalpha = dotp(reflect, &newray.dir);
  241.         Lighting(costheta, cosalpha, &lcolor, &surf->diff,
  242.                 &surf->spec, surf->srexp, color);
  243.     }
  244. }
  245.  
  246. /*
  247.  * Compute shading function (diffuse reflection and specular highlight)
  248.  *
  249.  * This function *adds* the computed color to "color".
  250.  */
  251. static void
  252. Lighting(costheta, cosalpha, lcolor, diff, spec, coef, color)
  253. Float costheta, cosalpha, coef;
  254. Color *diff, *spec, *color, *lcolor;
  255. {
  256.     Float intens;
  257.  
  258.     /*
  259.      * Diffuse reflection.
  260.      * Falls off as the cosine of the angle between
  261.      * the normal and the ray to the light (costheta).
  262.      */
  263.     color->r += diff->r * costheta * lcolor->r;
  264.     color->g += diff->g * costheta * lcolor->g;
  265.     color->b += diff->b * costheta * lcolor->b;
  266.     /*
  267.      * Specularly reflected highlights.
  268.      * Fall off as the cosine of the angle
  269.      * between the reflected ray and the ray to the light source.
  270.      */
  271.     if (coef < EPSILON || cosalpha <= 0.)
  272.         return;
  273.     /*
  274.      * Specular highlight = cosine of the angle raised to the
  275.      * appropriate power.
  276.      */
  277.     intens = pow(cosalpha, coef);
  278.     color->r += spec->r * intens * lcolor->r;
  279.     color->g += spec->g * intens * lcolor->g;
  280.     color->b += spec->b * intens * lcolor->b;
  281. }
  282.  
  283. /*
  284.  * Spawn a transmitted ray.  Returns TRUE if total internal reflection
  285.  * occurs, FALSE otherwise.
  286.  */
  287. static int
  288. TransmitRay(ray, pos, norm, k, index, statten, enter, back, contrib, intens, color)
  289. Ray *ray;
  290. Vector *pos, *norm;
  291. Float k, index, statten;
  292. int enter;
  293. Color *back, *contrib, *intens, *color;
  294. {
  295.     int total_int_refl = FALSE;
  296.     Ray NewRay;
  297.     Float dist, cosi, cosr;
  298.     Color newcol;
  299.     HitList hittmp;        /* Geom intersection record */
  300.  
  301.         NewRay.type = REFRACT_RAY;
  302.     NewRay.pos = *pos;        /* Origin == hit point */
  303.     NewRay.media = ray->media;    /* Media == old media */
  304.     NewRay.sample = ray->sample;
  305.     NewRay.time = ray->time;
  306.     NewRay.depth = ray->depth + 1;
  307.  
  308.     if (enter) {
  309.         /*
  310.          * Entering surface.
  311.          */
  312.         if (Refract(&NewRay.dir,
  313.             NewRay.media ? NewRay.media->index :
  314.             TopMedium.index, index, &ray->dir, norm, k)) {
  315.             total_int_refl = TRUE;
  316.         } else {
  317.             /*
  318.              * Push information for new medium.
  319.              */
  320.             NewRay.media = MediumPush(index, statten, NewRay.media);
  321.         }
  322.     } else {
  323.         /*
  324.          * Exiting surface
  325.          * Pop medium from stack.
  326.          */
  327.         if (NewRay.media != (Medium *)0)
  328.             NewRay.media = NewRay.media->next;
  329.         if (Refract(&NewRay.dir, index,
  330.             NewRay.media ? NewRay.media->index :
  331.             TopMedium.index, &ray->dir, norm, k)) {
  332.             total_int_refl = TRUE;
  333.         }
  334.     }
  335.  
  336.     /*
  337.      * At this point, NewRay.media is the medium into which
  338.      * the new ray is entering.
  339.      */
  340.  
  341.     if (!total_int_refl) {
  342. /* good for planar refraction only */
  343. /* cosi = cos angle(incident ray, surface normal)
  344.  * cosr = cos angle(refracted ray, surface normal) */
  345.                 cosi = -dotp(&ray->dir, norm);                
  346.         if (cosi < EPSILON) cosi = EPSILON;
  347.                 cosr = -dotp(&NewRay.dir, norm);                
  348. /*
  349.  *                       nr   cosr  2
  350.  * NewRay.viewdist = y = -- ( ---- )  * x 
  351.  *                       ni   cosi
  352.  * where x = distance newray origin to oldray viewpoint.
  353.  */
  354.                 NewRay.viewdist = (NewRay.media ? NewRay.media->index : TopMedium.index) / (ray->media ? ray->media->index : TopMedium.index) * 
  355.                                 (cosr * cosr) / (cosi * cosi) * 
  356.                                 (ray->viewdist < 0. ? -1. : 1.) * VecDist(&ray->viewpoint, &NewRay.pos);
  357. /* NewRay.viewpoint = NewRay.pos - y * NewRay.dir */
  358.                 VecAddScaled(NewRay.pos, -NewRay.viewdist, NewRay.dir, &NewRay.viewpoint);
  359. /* both rays "see" same area at intersection point */
  360.                 NewRay.width = PixelSize(ray, VecDist(&ray->pos, &NewRay.pos)) * cosr / cosi;
  361.                 NewRay.stretch = 1.;
  362.  
  363.         Stats.RefractRays++;
  364.         hittmp.nodes = 0;
  365.         dist = FAR_AWAY;
  366.         TraceRay(&NewRay, &hittmp, EPSILON, &dist);
  367.         ShadeRay(&hittmp, &NewRay, dist, back, &newcol, contrib);
  368.         ColorMultiply(newcol, *intens, &newcol);
  369.         /*
  370.          * Attenuate transmitted color.  Note that
  371.          * if the transmitted ray hit nothing, we still
  372.          * perform this computation, as it's possible
  373.          * that 'air' has a non-unit statten.
  374.          */
  375.         statten = NewRay.media ? NewRay.media->statten :
  376.             TopMedium.statten;
  377.         if (statten != 1.0) {
  378.             statten = pow(statten, dist);
  379.             ColorScale(statten, newcol, &newcol);
  380.         }
  381.         ColorAdd(*color, newcol, color);
  382.         /* Free pushed medium */
  383.         if (enter)
  384.             free((voidstar)NewRay.media);
  385.     }
  386.  
  387.     return total_int_refl;
  388. }
  389.  
  390. static void
  391. ReflectRay(ray, pos, dir, back, intens, contrib, color)
  392. Ray *ray;
  393. Vector *pos, *dir;
  394. Color *back, *intens, *contrib, *color;
  395. {
  396.     Ray NewRay;
  397.     HitList hittmp;        /* Geom intersection record */
  398.     Color newcol;
  399.     Float dist;
  400.  
  401.         NewRay.type = REFLECT_RAY;
  402.     NewRay.pos = *pos;        /* Origin == hit point */
  403.     NewRay.dir = *dir;        /* Direction == reflection */
  404.     NewRay.media = ray->media;    /* Medium == old medium */
  405.     NewRay.sample = ray->sample;
  406.     NewRay.time = ray->time;
  407.     NewRay.depth = ray->depth + 1;
  408.  
  409. /* good for planar reflections only */
  410. /* new viewpoint is mirrored old one. width at intersection point is the same */
  411.         NewRay.viewdist = (ray->viewdist < 0. ? -1. : 1.) * VecDist(&ray->viewpoint, &NewRay.pos);
  412.         NewRay.width = PixelSize(ray, VecDist(&ray->pos, &NewRay.pos));
  413.         VecAddScaled(NewRay.pos, -NewRay.viewdist, NewRay.dir, &NewRay.viewpoint);
  414.         NewRay.stretch = 1.;
  415.  
  416.     Stats.ReflectRays++;
  417.     hittmp.nodes = 0;
  418.     dist = FAR_AWAY;
  419.     (void)TraceRay(&NewRay, &hittmp, EPSILON, &dist);
  420.     ShadeRay(&hittmp, &NewRay, dist, back, &newcol, contrib);
  421.     ColorMultiply(newcol, *intens, &newcol);
  422.     ColorAdd(*color, newcol, color);
  423. }
  424.  
  425.